package org.hxyjs.config;


import org.hxyjs.shiro.QuartzSessionValidationScheduler2;
import org.hxyjs.shiro.Realm.CustomRealm;
import org.hxyjs.shiro.RetryLimitCredentialsMatcher;
import org.hxyjs.shiro.filter.CustomFormAuthenticationFilter;
import org.hxyjs.shiro.filter.KickoutSessionControlFilter;
import org.hxyjs.shiro.filter.SystemLogoutFilter;
import net.sf.ehcache.CacheManager;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.io.ResourceUtils;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Lazy;
import org.springframework.web.filter.DelegatingFilterProxy;

import javax.servlet.Filter;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;


/**
 * shiro-springboot 配置
 *
 * @author around
 * @date 2018-4-12
 */
//@Configuration
public class ShiroConfig {

    private static final Logger log = LoggerFactory.getLogger(ShiroFilterFactoryBean.class);

    /**
     * boot 需要的shiroFilter代理类
     * @return
     */
    @Bean
    public FilterRegistrationBean delegatingFilterProxy(){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        DelegatingFilterProxy proxy = new DelegatingFilterProxy();
        proxy.setTargetFilterLifecycle(true);
        proxy.setTargetBeanName("shiroFilter");
        filterRegistrationBean.setFilter(proxy);
        return filterRegistrationBean;
    }


    /**
     * 自定义shiro认证容器
     *
     * @return ShiroRealm
     */
    /**
     * 过滤器，Filter工厂，设置对应的过滤条件和跳转条件
     *
     * @param securityManager
     * @return
     */
    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(
            @Qualifier("securityManager") SecurityManager securityManager,
            @Qualifier("formAuthenticationFilter") CustomFormAuthenticationFilter formAuthenticationFilter,
            @Qualifier("logoutFilter") LogoutFilter logoutFilter,
            @Qualifier("kickoutSessionControlFilter") KickoutSessionControlFilter kickoutFilter) {
        log.info("shiroFilterFactoryBean init...");
        ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
        filter.setSecurityManager(securityManager);

        //插入自定义过滤器
        Map<String, Filter> filterMap = new LinkedHashMap<String, Filter>();

        //将自定义 的FormAuthenticationFilter注入shiroFilter中 //
        filterMap.put("authc",formAuthenticationFilter);
        filterMap.put("logout", logoutFilter);
        filterMap.put("kickout", kickoutFilter);
        filter.setFilters(filterMap);

        //登录，如果检查到你需要重新登录  你到那个地址去登录
        filter.setLoginUrl("/login");

        //filter.setSuccessUrl("/login");
        //首页
        //filter.setSuccessUrl("/index");
        //错误页面，认证不通过跳转
        filter.setUnauthorizedUrl("/error/403.jsp");
        //设置过滤器
        Map<String, String> map = new LinkedHashMap<String, String>();
        //过滤器资源放行内容
       //过虑器链定义，从上向下顺序执行，一般将/**放在最下边
         map.put("/favicon.ico","anon");
         map.put("/html/**", "anon");
         map.put("/login", "anon");
         map.put("/images/**", "anon");
         map.put("/static/**", "anon");
         //对错误页面放行
         map.put("/css/**","anon");

//        //游客，开发权限
//        map.put("/guest/**", "anon");
//        //用户，需要角色权限 “user”
//        map.put("/user/**", "roles[user]");
//        //管理员，需要角色权限 “admin”
//        map.put("/admin/**", "roles[admin]");
        //开放登陆接口

        map.put("/favicon.ico","anon");


        //验证码，可匿名访问
        map.put("/js/**","anon");
        map.put("/templates/**","anon");
        map.put("/login**", "anon");
        map.put("/error/**", "anon");




        //记住我
        map.put("memberController/members/page/json?modelName=member", "user");


        //注销 请求 logout.action地址，shiro去清除session
        //logout url归 logoutFilter管 在shirofilter配置的
        map.put("/logout", "logout");


        //** = authc 所有url都必须认证通过才可以访问
//        map.put("/showChart.jsp", "authc");

         map.put("/*Controller/**", "authc");

        filter.setFilterChainDefinitionMap(map);
        filter.getFilterChainDefinitionMap().forEach((key,value)->{
            System.out.println(key + " "+ value);
        });
        System.out.println("dddddddddddddddddd11111111111111111111111");
        return filter;
    }

    @Bean(name = "shiroRealm")
    @Lazy
    public CustomRealm getShiroRealm(@Qualifier("credentialsMatcher") CredentialsMatcher credentialsMatcher) {
        CustomRealm realm = new CustomRealm();
        realm.setCredentialsMatcher(credentialsMatcher);
        return realm;
    }

    /**
     * ehCacheManager 缓存管理器
     *
     * @return
     */

    @Bean(name = "ehCacheManager")
    public EhCacheManager getEhCacheManager() {
        CacheManager cacheManager = CacheManager.getCacheManager("es");
        if(cacheManager == null){
            try {
                cacheManager = CacheManager.create(ResourceUtils.getInputStreamForPath("classpath:config/ehcache-shiro.xml"));
            } catch (IOException e) {
                throw new RuntimeException("initialize cacheManager failed");
            }
        }
        EhCacheManager ehCacheManager = new EhCacheManager();
        ehCacheManager.setCacheManager(cacheManager);
        return ehCacheManager;
    }
    //强制单例模式 否则 热部署（上面ehCacheManager）生成多个 缓存对象，报错，其它sprin对象允许，缓存框架不允许
    //需要配合 lifecycleBeanPostProcessor 和
//    @Bean(name = "ehCacheManager")
//    public EhCacheManagerFactoryBean ehCacheManagerFactoryBean(){
//        EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean();
//        ehCacheManagerFactoryBean.setShared(true);
//        ehCacheManagerFactoryBean.isSingleton();
//        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
//            ehCacheManagerFactoryBean.setConfigLocation(resolver.getResource("classpath:config/ehcache.xml"));
//        return ehCacheManagerFactoryBean;
//    }

    /**
     * 自定义凭证管理器
     *
     * @param ehCacheManager
     * @return
     */
    @Bean("credentialsMatcher")
    public RetryLimitCredentialsMatcher getCredentialsMatcher(EhCacheManager ehCacheManager) {
        RetryLimitCredentialsMatcher retryLimitCredentialsMatcher = new RetryLimitCredentialsMatcher(ehCacheManager);
        retryLimitCredentialsMatcher.setHashAlgorithmName("md5");
        retryLimitCredentialsMatcher.setHashIterations(3);
        retryLimitCredentialsMatcher.setStoredCredentialsHexEncoded(true);
        return retryLimitCredentialsMatcher;
    }

    /**
     * 自定义form认证过虑器
     * 基于Form表单的身份验证过滤器，不配置将也会注册此过虑器，表单中的用户账号、密码及loginurl将采用默认值，建议配置
     *
     * @return
     */
    @Bean("formAuthenticationFilter")
    public CustomFormAuthenticationFilter getformAuthenticationFilter() {
        CustomFormAuthenticationFilter customFormAuthenticationFilter = new CustomFormAuthenticationFilter();
        customFormAuthenticationFilter.setUsernameParam("username");
        customFormAuthenticationFilter.setPasswordParam("password");
        customFormAuthenticationFilter.setRememberMeParam("rememberMe");
        return customFormAuthenticationFilter;
    }

    /**
     * 配置自定义sessionID session模板
     *
     * @return
     */
    @Bean(name = "sessionIdcookies")
    public SimpleCookie getSessionIdCookie() {
        SimpleCookie sessionIdCookie = new SimpleCookie("com.airui.session.id");
        sessionIdCookie.setHttpOnly(true);
        sessionIdCookie.setMaxAge(9000);
        sessionIdCookie.setName("shiro.session");
        return sessionIdCookie;
    }


    /**
     * 自定义RememberMe cookie
     *
     * @return
     */
    @Bean("rememberMeCookie")
    public SimpleCookie getRememberMeCookie() {
        log.info("rememberMeCookie init...");
        // 这个参数是cookie的名称，对应前端的checkbox 的name = rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        // <!-- 记住我cookie生效时间30天（259200） ,单位秒;-->
        simpleCookie.setHttpOnly(true);
        simpleCookie.setMaxAge(7 * 24 * 60 * 60);
        simpleCookie.setName("remeber.session");
        return simpleCookie;
    }

    /**
     * 保持会话记录管理器
     *
     * @return
     */
    @Bean(name = "rememberMeManager")
    public CookieRememberMeManager rememberMeManager() {
        log.info("rememberMeManager init...");
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(getRememberMeCookie());
        cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
        return cookieRememberMeManager;
    }

    /**
     * shiro-session仓库
     *
     * @return
     */
    @Bean(name = "sessionDao")
    public SessionDAO getSessionDAO() {
        EnterpriseCacheSessionDAO sessionDao = new EnterpriseCacheSessionDAO();
        sessionDao.setActiveSessionsCacheName("shiro-activeSessionCache");
        sessionDao.setSessionIdGenerator(new JavaUuidSessionIdGenerator());
        return sessionDao;
    }

    /**
     * 保证实现了Shiro内部lifecycle函数的bean执行
     */
    @Bean("lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }
    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }



//Qualifier 可能存在多个匹配的指定名称
    /**
     * 会话session管理
     *
     * @return
     */
    @Bean(name = "sessionManager") //@Qualifier("sessionIdcookies")SimpleCookie cookie
    public SessionManager getSessionManager(
            @Qualifier("ehCacheManager") EhCacheManager ehCacheManager,
            @Qualifier("sessionDao") SessionDAO sessionDao,
            @Qualifier("sessionValidationScheduler") QuartzSessionValidationScheduler2 sessionValidationScheduler) {
        log.info("SessionManager init...");
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        //session失效时间
        sessionManager.setGlobalSessionTimeout(900000);
        //清理失效会话
        sessionManager.setSessionValidationInterval(600000);
        sessionManager.setDeleteInvalidSessions(true);
        sessionManager.setSessionValidationSchedulerEnabled(true);
        sessionManager.setSessionValidationScheduler(sessionValidationScheduler);
        //自定义cookies 启用
        sessionManager.setSessionIdCookieEnabled(true);
        sessionManager.setSessionIdCookie(getSessionIdCookie());

        sessionValidationScheduler.setSessionManager(sessionManager);
        sessionManager.setSessionValidationInterval(900000);
        sessionManager.setSessionIdUrlRewritingEnabled(false);

        sessionManager.setCacheManager(ehCacheManager);
        sessionManager.setSessionDAO(sessionDao);

        return sessionManager;
    }
    /**
     * quartz 2.x版本用
     *
     * @param
     * @return
     */
    @Bean("sessionValidationScheduler")
    public QuartzSessionValidationScheduler2 getQuartzSessionValidationScheduler2() {
        QuartzSessionValidationScheduler2 quartzSessionValidationScheduler2 = new QuartzSessionValidationScheduler2();
        //quartzSessionValidationScheduler2.setSessionManager((ValidatingSessionManager) sessionManager);
        quartzSessionValidationScheduler2.setSessionValidationInterval(1800000);
        return quartzSessionValidationScheduler2;
    }
    /**
     * 安全管理器，权限管理，配置主要是Realm的管理认证
     *
     * @param realm
     * @param sessionManager
     * @param rememberMeManager
     * @return
     */
    @Bean(name = "securityManager")
    public SecurityManager securityManager(@Qualifier("shiroRealm") CustomRealm realm,
                                           @Qualifier("ehCacheManager") EhCacheManager ehCacheManager,
                                           @Qualifier("sessionManager") SessionManager sessionManager,
                                           @Qualifier("rememberMeManager") CookieRememberMeManager rememberMeManager) {
        log.info("securityManager init...");
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm);

        securityManager.setRememberMeManager(rememberMeManager);
        securityManager.setSessionManager(sessionManager);
        securityManager.setCacheManager(ehCacheManager);

        return securityManager;
    }



    /**
     * 构造踢出用户过滤器
     *
     * @return
     */
    //@Bean(name = "kickoutSessionControlFilter")
    public KickoutSessionControlFilter getKickoutSessionControlFilter(
            @Qualifier("ehCacheManager") EhCacheManager ehCacheManager,
            @Qualifier("sessionManager") SessionManager sessionManager) {
        KickoutSessionControlFilter filter = new KickoutSessionControlFilter();
        filter.setKickoutAfter(false);
        filter.setMaxSession(1);
        filter.setKickoutUrl("/kickout");
        System.out.println("kickout init");
        //filter.setKickoutUrl("/tqm/user/login.html?kickout=1");
        filter.setCacheManager(ehCacheManager);
        filter.setSessionManager(sessionManager);
        return filter;
    }

//    /**
//     * 注册 注销过滤器
//     * @return
//     */
//    @Bean("logoutFilter")
//        public SystemLogoutFilter getSystemLogoutFilter(){
//        System.out.println("mmmm11111");
//    SystemLogoutFilter systemLogoutFilter = new SystemLogoutFilter();
//    systemLogoutFilter.setRedirectUrl("/login");
//        return systemLogoutFilter;
//}



    @Bean(name = "AuthorizationAttributeSourceAdvisor")
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
            @Qualifier("securityManager") SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }



    @Bean
    @ConditionalOnMissingBean
    public CacheManager ehCacheCacheManager() {
        System.out.println("iiii");
        return CacheManager.create();
    }


    /**
     * @desc 方法名字可以随便取
     * @param filter
     * @return
     */
    @Bean
    public FilterRegistrationBean<SystemLogoutFilter> shiroCheckSessionRegistration(SystemLogoutFilter filter) {
        FilterRegistrationBean<SystemLogoutFilter> registration = new FilterRegistrationBean<SystemLogoutFilter>(filter);
        registration.setEnabled(false);
        return registration;
    }

}
